/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.acworks.acroute;

import com.acworks.acroute.impl.RouteImpl;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author nikita
 */
public class RouteBuilder {

    final private Datacenter datacenter;
    private List<Route> validRoutes;
    private int traversedRoutesCounter;
    private boolean routesComputed = false;
    final private static Logger LOG = LoggerFactory.getLogger(RouteBuilder.class);

    public RouteBuilder(Datacenter dc) {
        assert dc != null;
        this.datacenter = dc;
        validRoutes = new ArrayList<Route>();
        traversedRoutesCounter = 0;
    }

    /**
     * compute all possible routes (valid or not)
     */
    public void buildAllRoutes() {
        if (routesComputed) {
            //already done;
            return;
        }

        final long startTime = System.nanoTime();

        Route initialRoute = new RouteImpl(datacenter);

        validRoutes.clear();
        computeValidRoutes(initialRoute, validRoutes);

        routesComputed = true;

        final long durationMillis = (System.nanoTime() - startTime) / 1000000;

        LOG.info("done: validRoutes=" + validRoutes.size() + " in " + durationMillis + " ms");
    }

    public int computeValidRouteCount() {
        if (!routesComputed) {
            buildAllRoutes();
        }
        return getValidRoutes().size();
    }

    /**
     * @param ds
     * @return all valid cooling duct routes for given datacenter
     */
    public List<Route> getValidRoutes() {
        if (!routesComputed) {
            throw new IllegalStateException("routes haven't been computed yet - call buildRoutes() first");
        } else {
            return validRoutes;
        }
    }

    private void computeValidRoutes(final Route r, final List<Route> successfulRoutes) {
        assert r != null;
        assert successfulRoutes != null;

        List<Space> nextRooms = r.getAdjacentUnvisitedOwnedRooms();
        if (nextRooms.isEmpty()) {
            //at the end of the route; only add if route is successful:
            if (r.isValid()) {
                successfulRoutes.add(r);
            }
            traversedRoutesCounter++;
            if (LOG.isDebugEnabled() && traversedRoutesCounter % 100000 == 0) {
                LOG.debug("traced " + traversedRoutesCounter + " routes so far; found " + successfulRoutes.size() + " valid routes");
            }
        } else {

            //record # of spaces in 'r' for potential branching later
            final int routeSpaceCount = r.getRouteSpaces().size();

            //advance to next space on the current route
            Space nextSpace = nextRooms.get(0);
            r.advanceToSpace(nextSpace);
            computeValidRoutes(r, successfulRoutes);

            if (nextRooms.size() > 1) {

                // multiple places to go -> create new routes to be traversed later
                for (int i = 1; i < nextRooms.size(); i++) {
                    Space potentialNextRoom = nextRooms.get(i);
                    Route unfinishedRoute = new RouteImpl(r, routeSpaceCount, potentialNextRoom);
                    computeValidRoutes(unfinishedRoute, successfulRoutes);
                }
            }
        }

        //help with GC
        nextRooms = null;
    }
}